package com.ug.system.service.impl;

import cn.hutool.core.util.StrUtil;
import com.ug.common.cache.redis.RedisCacheService;
import com.ug.common.constant.CaptchaConstants;
import com.ug.common.constant.CommonConstants;
import com.ug.common.constant.SecurityConstants;
import com.ug.common.enums.ErrorCode;
import com.ug.common.exception.SystemException;
import com.ug.common.utils.EncryptionUtil;
import com.ug.common.utils.JwtUtil;
import com.ug.common.utils.StringUtil;
import com.ug.config.security.JwtUser;
import com.ug.system.entity.SysRole;
import com.ug.system.entity.SysUser;
import com.ug.system.entity.vo.SysUserVO;
import com.ug.system.entity.dto.LoginDto;
import com.ug.system.service.LoginService;
import com.ug.system.service.SysRoleService;
import com.ug.system.service.SysUserService;
import com.wf.captcha.ArithmeticCaptcha;
import com.wf.captcha.base.Captcha;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @Author LY
 * @create 2024/8/5 10:33
 * @Description 登录服务实现
 */
@RequiredArgsConstructor
@Service
public class LoginServiceImpl implements LoginService {

    private static final Logger logger = LoggerFactory.getLogger(LoginServiceImpl.class);

    private final SysUserService userService;

    private final SysRoleService roleService;

    private final AuthenticationManager authenticationManager;

    private final RedisCacheService redisCacheService;

    @Override
    public JwtUser doLogin(LoginDto loginDto) {
        logger.info("用户登录：{}", loginDto.getUsername());
        //  校验验证码
        if (StringUtil.isBlank(loginDto.getCode()) || !checkCode(loginDto.getUuid(), loginDto.getCode())){
            throw new SystemException(ErrorCode.CAPTCHA_VERIFY_ERROR);
        }
        if (StringUtil.isBlank(loginDto.getUsername())){
            throw new SystemException(ErrorCode.USERNAME_IS_NULL);
        }
        if (StringUtil.isBlank(loginDto.getPassword())){
            throw new SystemException(ErrorCode.PASSWORD_IS_NULL);
        }
        //  根据用户名查询用户是否存在
        SysUser user = userService.findByName(loginDto.getUsername());
        if (user == null){
            throw new SystemException(ErrorCode.USERNAME_OR_PASSWORD_ERROR);
        }
        //  密文密码解密
        String decryptPassword = EncryptionUtil.desEncrypt(loginDto.getPassword());
        return getJwtUser(user, loginDto.getUsername(), decryptPassword, false);
    }

    @Override
    public JwtUser quickLogin(String uuid) {
        logger.info("第三方快捷登录：{}", uuid);
        String openId = redisCacheService.getString(uuid);
        if (StringUtil.isBlank(uuid) || StringUtil.isBlank(openId)){
            throw new SystemException(ErrorCode.PARAMS_INVALID);
        }
        //  根据openid获取用户信息
        SysUser user = userService.findByOpenId(openId);
        if (user == null){
            throw new SystemException(ErrorCode.QUICK_LOGIN_ERROR);
        }
        // 获取jwt信息，由于security认证模式需要密码这里使用默认密码校验
        return getJwtUser(user, user.getOpenId(), CommonConstants.DEFAULT_PASSWORD, true);
    }

    @Override
    public String refreshToken(HttpServletRequest request) {
        //  通过请求头获取refreshToken
        String refreshToken = request.getHeader(SecurityConstants.TOKEN_HEADER);
        // 如果refreshToken存在
        if (StrUtil.isNotBlank(refreshToken)){
            //  去掉头部，得到原始refreshToken
            refreshToken = refreshToken.replaceFirst(SecurityConstants.TOKEN_PREFIX, "");
            //  解析refreshToken,得到用户名
            Claims claims = JwtUtil.getRefreshTokenBody(refreshToken);
            //  如果refreshToken还在有效期内
            if (claims.get("exp", Long.class) > 0){
                //  获取当前用户信息
                SysUser user = userService.findByUserId(claims.get(SecurityConstants.TOKEN_USER_ID_CLAIM, Long.class));
                //  获取当前用户角色
                List<String> roles = getRolesByUserId(user.getUserId());
                String name = StrUtil.isEmpty(user.getUsername()) ? user.getNickName():user.getUsername();
                //  重新获取token
                return JwtUtil.generateToken(user.getUserId(), name, roles, false);
            }
        }
        return "";
    }

    @Override
    public Map<String, Object> getVerifyCode() {
        // 获取运算的结果
        Captcha captcha = new ArithmeticCaptcha(CaptchaConstants.width, CaptchaConstants.height);
        String uuid = UUID.randomUUID().toString().replace("-", "");
        //当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        if (captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 缓存验证码信息,时间1分钟
        redisCacheService.put(uuid, captchaValue, 60);
        // 验证码信息
        return new HashMap<>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
    }

    /**
     * security登录校验
     * @param user 用户信息
     * @param username 输入的用户名/快捷登录的openId
     * @param password 输入的密码
     * @param isQuick 是否快捷登录
     */
    private JwtUser getJwtUser(SysUser user, String username, String password, boolean isQuick) {
        //  判断密码是否正确
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(username, password);
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        //  将认证信息设置到SpringSecurity上下文中
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //  获取当前用户角色
        List<String> roles = getRolesByUserId(user.getUserId());
        //  获取当前用户别名，快捷登录时取昵称
        String name = isQuick ? user.getNickName() : user.getUsername();
        //  生成token
        String token = JwtUtil.generateToken(user.getUserId(), name, roles, false);
        //  生成refresh_token
        String refreshToken = JwtUtil.getRefreshToken(user.getUserId(), name);
        //  用户信息
        SysUserVO userDto = new SysUserVO();
        userDto.setUsername(user.getUsername());
        userDto.setEmail(user.getEmail());
        userDto.setNickName(user.getNickName());
        userDto.setRoles(roles);
        return new JwtUser(token, refreshToken, userDto);
    }

    /**
     * 获取当前用户角色列表
     * @param userId 用户id
     */
    private List<String> getRolesByUserId(Long userId){
        //  获取当前用户角色对象
        List<SysRole> sysRoles = roleService.getRoleByUserId(userId);
        //  角色
        List<String> roles = new ArrayList<>();
        sysRoles.forEach( sysRole -> roles.add(sysRole.getRoleCode()));
        return roles;
    }

    /**
     * 验证码校验
     */
    private boolean checkCode(String uuid, String code){
        boolean b = false;
        String uCode = redisCacheService.getString(uuid);
        if (StrUtil.isNotEmpty(uCode) && StrUtil.equals(uCode, code)){
            b = true;
            redisCacheService.delete(uuid);
        }
        return b;
    }
}
